import { Command } from 'commander';
import inquirer from 'inquirer';
import semver from 'semver';
import { api } from '../services/api';
import { config } from '../services/config';
import { logger } from '../utils/logger';
import { createTable, formatDate } from '../utils/table';
import { requireAuth, requireProject, validateModuleName } from '../utils/validation';
import { CLIError } from '../types';

export function moduleCommands(program: Command): void {
  program
    .command('add <module>')
    .description('Install a module to current project/environment')
    .option('-v, --version <version>', 'Specific version to install')
    .option('-e, --env <env>', 'Target environment')
    .action(async (moduleName: string, options) => {
      try {
        requireAuth();
        const projectId = requireProject();
        
        let targetEnv = options.env || config.getCurrentEnvironment();
        const [name, version] = moduleName.includes('@') ? moduleName.split('@') : [moduleName, options.version];

        const spinner = logger.spinner(`Installing module ${name}${version ? `@${version}` : ''}...`);
        try {
          const environments = await api.getEnvironments(projectId);
          const environment = environments.find(e => e.name === targetEnv);
          
          if (!environment) {
            throw new CLIError(`Environment "${targetEnv}" not found`);
          }

          await api.installModule(projectId, environment.id, name, version);
          spinner.succeed();
          
          logger.success(`Module "${name}" installed successfully to ${targetEnv}`);
        } catch (error) {
          spinner.fail();
          throw error;
        }
      } catch (error) {
        if (error instanceof CLIError) {
          throw error;
        }
        throw new CLIError('Failed to install module');
      }
    });

  program
    .command('remove <module>')
    .description('Remove a module from current environment')
    .option('-e, --env <env>', 'Target environment')
    .action(async (moduleName: string, options) => {
      try {
        requireAuth();
        const projectId = requireProject();
        
        let targetEnv = options.env || config.getCurrentEnvironment();

        const confirm = await inquirer.prompt([
          {
            type: 'confirm',
            name: 'remove',
            message: `Remove module "${moduleName}" from environment "${targetEnv}"?`,
            default: false
          }
        ]);

        if (!confirm.remove) {
          logger.info('Operation cancelled');
          return;
        }

        const spinner = logger.spinner(`Removing module ${moduleName}...`);
        try {
          const environments = await api.getEnvironments(projectId);
          const environment = environments.find(e => e.name === targetEnv);
          
          if (!environment) {
            throw new CLIError(`Environment "${targetEnv}" not found`);
          }

          await api.removeModule(projectId, environment.id, moduleName);
          spinner.succeed();
          
          logger.success(`Module "${moduleName}" removed successfully from ${targetEnv}`);
        } catch (error) {
          spinner.fail();
          throw error;
        }
      } catch (error) {
        if (error instanceof CLIError) {
          throw error;
        }
        throw new CLIError('Failed to remove module');
      }
    });

  program
    .command('list')
    .description('List installed modules')
    .option('-e, --env <env>', 'Target environment')
    .action(async (options) => {
      try {
        requireAuth();
        const projectId = requireProject();
        
        let targetEnv = options.env || config.getCurrentEnvironment();

        const spinner = logger.spinner('Fetching modules...');
        try {
          const environments = await api.getEnvironments(projectId);
          const environment = environments.find(e => e.name === targetEnv);
          
          if (!environment) {
            throw new CLIError(`Environment "${targetEnv}" not found`);
          }

          spinner.succeed();

          if (!environment.modules || environment.modules.length === 0) {
            logger.info(`No modules installed in environment "${targetEnv}"`);
            return;
          }

          const headers = ['Module', 'Version', 'Environment'];
          const rows = environment.modules.map(mod => [
            mod.moduleId,
            mod.version,
            mod.environment
          ]);

          logger.log(`\nInstalled modules in "${targetEnv}":`);
          logger.log(createTable(headers, rows));
        } catch (error) {
          spinner.fail();
          throw error;
        }
      } catch (error) {
        if (error instanceof CLIError) {
          throw error;
        }
        throw new CLIError('Failed to list modules');
      }
    });

  program
    .command('search <keyword>')
    .description('Search modules in the marketplace')
    .action(async (keyword: string) => {
      try {
        requireAuth();

        const spinner = logger.spinner(`Searching for "${keyword}"...`);
        try {
          const modules = await api.searchModules(keyword);
          spinner.succeed();

          if (modules.length === 0) {
            logger.info(`No modules found matching "${keyword}"`);
            return;
          }

          const headers = ['Name', 'Version', 'Author', 'Description', 'Tags'];
          const rows = modules.map(mod => [
            mod.name,
            mod.version,
            mod.author,
            (mod.description || '').substring(0, 50) + (mod.description && mod.description.length > 50 ? '...' : ''),
            mod.tags.slice(0, 3).join(', ')
          ]);

          logger.log(`\nFound ${modules.length} modules:`);
          logger.log(createTable(headers, rows));
        } catch (error) {
          spinner.fail();
          throw error;
        }
      } catch (error) {
        if (error instanceof CLIError) {
          throw error;
        }
        throw new CLIError('Failed to search modules');
      }
    });

  program
    .command('info <module>')
    .description('Show detailed information about a module')
    .action(async (moduleName: string) => {
      try {
        requireAuth();

        const spinner = logger.spinner(`Fetching module information...`);
        try {
          const module = await api.getModule(moduleName);
          spinner.succeed();

          logger.log(`\nModule Information:`);
          logger.log(`Name: ${module.name}`);
          logger.log(`Version: ${module.version}`);
          logger.log(`Author: ${module.author}`);
          logger.log(`Description: ${module.description || 'No description'}`);
          logger.log(`Created: ${formatDate(module.createdAt)}`);
          logger.log(`Updated: ${formatDate(module.updatedAt)}`);
          
          if (module.tags && module.tags.length > 0) {
            logger.log(`Tags: ${module.tags.join(', ')}`);
          }

          if (module.dependencies && module.dependencies.length > 0) {
            logger.log(`\nDependencies:`);
            module.dependencies.forEach(dep => {
              logger.log(`  • ${dep}`);
            });
          }

          if (module.actions && module.actions.length > 0) {
            logger.log(`\nAvailable Actions:`);
            module.actions.forEach(action => {
              logger.log(`  • ${action.name}${action.description ? ` - ${action.description}` : ''}`);
            });
          }
        } catch (error) {
          spinner.fail();
          throw error;
        }
      } catch (error) {
        if (error instanceof CLIError) {
          throw error;
        }
        throw new CLIError('Failed to get module information');
      }
    });

  program
    .command('upgrade <module>')
    .description('Upgrade a module to the latest version')
    .option('-v, --version <version>', 'Specific version to upgrade to')
    .option('-e, --env <env>', 'Target environment')
    .action(async (moduleName: string, options) => {
      try {
        requireAuth();
        const projectId = requireProject();
        
        let targetEnv = options.env || config.getCurrentEnvironment();

        const spinner = logger.spinner(`Upgrading module ${moduleName}...`);
        try {
          const environments = await api.getEnvironments(projectId);
          const environment = environments.find(e => e.name === targetEnv);
          
          if (!environment) {
            throw new CLIError(`Environment "${targetEnv}" not found`);
          }

          const currentModule = environment.modules?.find(m => m.moduleId === moduleName);
          if (!currentModule) {
            throw new CLIError(`Module "${moduleName}" is not installed in "${targetEnv}"`);
          }

          await api.removeModule(projectId, environment.id, moduleName);
          await api.installModule(projectId, environment.id, moduleName, options.version);
          
          spinner.succeed();
          logger.success(`Module "${moduleName}" upgraded successfully in ${targetEnv}`);
        } catch (error) {
          spinner.fail();
          throw error;
        }
      } catch (error) {
        if (error instanceof CLIError) {
          throw error;
        }
        throw new CLIError('Failed to upgrade module');
      }
    });
}